--[[
Checks for undeclared global variables. All global variables must be declared either by calling 
global() or assigning to the variable in a main chunk before using it anywhere else.
--]]
module(..., package.seeall)


local strfmt = string.format
local mt = getmetatable(_G)
if mt == nil then
    mt = {}
    setmetatable(_G, mt)
end

__STRICT = true           -- boolean flag that controls strict mode
mt.__declared = {}        -- set of declared global variables
for k, _ in pairs(_G) do  -- mark all existent global variables as declared
    mt.__declared[k] = true
end

mt.__newindex = function(t, n, v)
    if __STRICT and not mt.__declared[n] then
        local what = debug.getinfo(2, "S").what
        if what ~= "main" and what ~= "C" then
            error(strfmt("assignment to undeclared global variable '%s' in %s chunk", n, what), 2)
        end
        mt.__declared[n] = true
    end
    rawset(t, n, v)
end

mt.__index = function(t, n)
    if __STRICT and not mt.__declared[n] and debug.getinfo(2, "S").what ~= "C" then
        error(strfmt("undeclared global variable '%s'", n), 2)
    end
    return rawget(t, n)
end

function global(...)
    --[[ Use this function to declare global variables in strict mode. ]]
    for _, v in ipairs{...} do 
        mt.__declared[v] = true 
    end
end

_G.global = global
